#ifndef TRINAMICCOMMUNICATION_H
#define TRINAMICCOMMUNICATION_H

#include <vector>
#include <map>
#include <Windows.h>
#include <QSettings>

#define PI 3.1415926

//Opcodes of all TMCL commands that can be used in direct mode
#define TMCL_ROR 1
#define TMCL_ROL 2
#define TMCL_MST 3
#define TMCL_MVP 4
#define TMCL_SAP 5
#define TMCL_GAP 6
#define TMCL_STAP 7
#define TMCL_RSAP 8
#define TMCL_SGP 9
#define TMCL_GGP 10
#define TMCL_STGP 11
#define TMCL_RSGP 12
#define TMCL_RFS 13
#define TMCL_SIO 14
#define TMCL_GIO 15
#define TMCL_SCO 30
#define TMCL_GCO 31
#define TMCL_CCO 32

//Opcodes of TMCL control functions (to be used to run or abort a TMCL program in the module)
#define TMCL_APPL_STOP 128
#define TMCL_APPL_RUN 129
#define TMCL_APPL_RESET 131

//Options for MVP commandds
#define MVP_ABS 0
#define MVP_REL 1
#define MVP_COORD 2

//Options for RFS command
#define RFS_START 0
#define RFS_STOP 1
#define RFS_STATUS 2

//Result codes for GetResult
#define TMCL_RESULT_OK 0
#define TMCL_RESULT_NOT_READY 1
#define TMCL_RESULT_CHECKSUM_ERROR 2

class MainWindow;

class TrinamicCommunication
{
public:
    TrinamicCommunication();
    ~TrinamicCommunication();

    void Initialize();
    void ProcessControllerMove(std::vector<float>& motionData);

    void SendCmd(HANDLE Handle, UCHAR Address, UCHAR Command, UCHAR Type, UCHAR Motor, INT Value);
    UCHAR GetResult(HANDLE Handle, UCHAR *Address, UCHAR *Status, int *Value);

    void SetupMotors();
    void StopMotors();
    void CheckVelocityBin(int axis, int speed);
    void SetVelocityBin(int axis, int bin, bool openloop = 0);
    void SetVelocity(int axis, int stepRate, bool openloop = 0);

    void TakeStrides(int axis, int nStrides, bool openloop = 0);

    void MoveToPosition(int axis, double position, bool openloop = 0);

    void MoveToPosition(int axis1, double position1,
                        int axis2, double position2,
                        int axis3, double position3,
                        bool openloop = 0);

    void MoveDeltaPosition(int axis1, double position1,
                           int axis2, double position2,
                           int axis3, double position3,
                           bool openloop = 0);

    bool MoveFinished();
    void SetTargetPosition(int axis, int position, bool openloop = 0);
    int AskForPosition(int axis);
    int CheckLeftSwitch(int axis);
    void PositionQuery();

    void SetCoarseFineSetting(int setting);
    void SetToAbsoluteMode();
    void SetToRelativeMode();
    void SetRelativeModeOrigin();

    void MoveToAngleRelative(double radians);
    void MoveThetaRadius(double radians);

    void SetMotorizedRotationOrigin();

    int CheckTrinamic();
    void SetBaudrate(int baudrate_code);


    void RestoreMaximumSpeed();

    void SetMaximumSpeed(int axis, int speed);

    void DisableHoldingCurrent(bool setting);

    HANDLE  hTrinamicSerial;
    //MainWindow*  hMainWindow;


    //REFER VERY CLOSELY TO TRINAMIC 310 FIRMWARE MANUAL FOR COMMAND REFERENCE

    //This class will have the following structure and function:
    //
    //1. Motor's current position in microsteps and microns are tracked
    //2. A relative origin and relative origin mode can be toggled, modifying display
    //3. (in the future, various position-related moves can be made)
    //4. Reference (limit switch) procedure can be initiated

    //Actual joystick control
    //
    //5. The controller's update rate via rs232 must be respected for responsive control
    //6. We track the controllers current velocity and last communication

    //7. Keyboard control allows single velocity control, starting the motion on downpress
    //8. and stopping the motion on uppress (must be bug free)

    //9. Joystick control gets HID input at high rate, but only changes the velocity when necessary,
    //10. communication may still be too rapid for oscillation between steps.  hysteresis?
    //11. Thus rather than throwing away HID data, any HID event can potentially result in a velocity command.
    //    and the HID position always maps to a roughly correct speed (esp zero).


    //query, TMCs microsteps
    int     CurrentPositionSteps[6];
    float   CurrentPositionMicrons[6];

    float   CurrentPositionRotated[6];
    float   LastPositionRotated[6];
    double  RotationModeRelativeOrigin[6];
    float   XZRotationAngle;
    bool    bRotationMode;
    bool    bMotorizedRotation;
    double  PivotRadius;

    int     CurrentCoarseFineSetting;

    bool    bRelativeMode;
    double     RelativeModeOrigin[6];

    //in TMCs units +/- 2047
    int     MaxSpeed; //2047

    //x = 0, y = 1, z = 2
    //and maps are kept for x y z for HID, and x y z for Trinamic

    float   CurrentVelocity[6];
    int     CurrentVelocityBin[6]; //goes from -nVelocityBins to nVelocityBins
    int     CurrentVelocityBinBoundaryLow[6];
    int     CurrentVelocityBinBoundaryHigh[6];
    float   MicronsPerSecondPerBin;
    float   TrinamicFactor;
    int     nVelocityBins[6];
    int     nVelocityPerBin[6];
    int     nVelocityHysteresis[6];

    int     nStepsPerStride[6];
    int     nCurrentPositionTarget[6];


    //motor parameters for axes
    int     Polarity[6];
    int     MaximumCurrent[6];              //set to around 200 (mA)        6
    int     FullStepThreshold[6];           //set to some value < 2048      211
    int     MicrostepMode[6];               //6 for 64 microstepping        140
    int     ReferenceSearchSpeed[6];        //zero for full speed           194
    int     ReferencingMode[6];             //2 for left and right          193
    int     RightLimitSwitchStopDisable[6]; //0 for active limit switch     12
    int     LeftLimitSwitchStopDisable[6];  //0 for active limit switch     13
    int     MaximumAcceleration[6];         //max acceleration              5
    int     MaximumSpeed[6];                //0-2047, default 1000          4
    int     SoftStopFlag[6];                //0 for failsafe action         149






    //these values determine the coordination of controller move and rs232 command
    DWORD   dwTimeOfLastMove;
    int     nMoveCount;

    //these values are essentially fixed.  Steps and microsteps should be considered interchangeable
    //for this class

    int     MicrostepRatio; //values of 1 to 10.
    float   MicronsPerStep[6];
    float   RadiansPerStep;
    int     XMotor;
    int     YMotor;
    int     ZMotor;
    int     ThetaMotor;
    int     nCommandCount;

    int     nErrorCount;

    QSettings* qSettings;


private slots:
};

#endif // TRINAMICCOMMUNICATION_H
